// 入口文件(第一个执行的程序所在的文件)

// 导入模块
let express = require('express')
let path = require('path')
let userRouter = require('./routers/userRouter')
let goodsRouter = require('./routers/goodsRouter')
let fileRouter = require('./routers/fileRouter')
let cors = require('cors')

// 连接数据库
require('./db/connect')

// 创建express服务
let app = express()

// 要添加的各种功能:


/* 
    session 会话(控制)
    http协议是无状态的

    cookie是客户端记录用户状态一种机制
    session是服务端记录用户状态一种机制

    cookie存储在浏览器
    session存储在服务器

    session分为两部分: session id 和 session data
    session id(加密字符串) 通过cookie保存在浏览器
    session data(session数据) 存储在服务器

    session的工作流程:
        1.前端发送登录请求
        2.后端接收登录请求
        3.后端与数据库比对账号密码
            当账号密码正确时:
                1.服务端创建session
                2.向session中存储数据(已登录的标识,用户信息等)
                3.服务端通过设置cookie,让客户端存储session id
        4.后端响应比对结果
        5.前端接收响应结果
        6.前端根据响应结果反馈给用户

        客户端再次访问服务器时,自动携带cookie到服务器(将session id发送到服务端)
        服务端通过 session id 找到对应的 session data 进行验证

    服务端如何去操作session?
    使用 express-session 插件
    express-session 插件会自动操作cookie(读取,设置)

    1.安装: npm i express-session
    2.导入: require()
    3.使用: app.use()
    app.use(session({配置}))
    使用插件之后,在req对象上添加了一个session属性(值是一个对象)
    向session中存储数据时,插件会自动生成一个session id(加密字符串)
    配置:{
        secret: '加密口令'
        ...
    }


*/
/* // 导入插件
let session = require('express-session')
// express服务使用express-session插件
app.use(session({// 配置
    secret: 'hehehaha',// 加密口令,必填
    saveUninitialized: false,// 不存储新创建还未添加数据的session
    resave: true,// 无论session是否被修改都重新存储
    name: 'nicai',// 设置浏览器存储session id 那个cookie的键名, 默认'connect.sid'
    cookie: {
        maxAge: 1000*60*30,// 有效期/毫秒
        domain: 'localhost',// 域名
        path: '/'// 路径
    }
})) */


/* token 象征 标志的意思
    token在计算机身份验证中是'令牌'的意思
    token像生活中讲的 '暗号' -> 天王盖地虎 提莫一米五

    token的工作流程:
        1.前端发送登录请求
        2.后端接收登录请求
        3.后端与数据库比对账号密码
            当账号密码正确时:(登录成功)
                后端生成token字符串(加密字符串表示已登录)
        4.后端响应比对结果
            响应登录结果并携带token字符串返回给前端
        5.前端接收响应结果
            将token字符串保存在客户端(cookie localStorage)
        6.前端根据响应结果反馈给用户

        客户端再次访问服务器时,获取本地存储中的token字符串在请求头携带发送到服务端
        服务端去验证token字符串,根据验证结果做出不同的响应
    
    使用 session 实现 保持登录状态:
        1.session需要存储空间(内存 数据库)
        2.cookie不能跨域使用

    使用 token 实现 保持登录状态:
        可以跨域使用
        不需存储空间(服务端保存的是一种加密规则)
        更适合分布式应用
        更适合实现单点登录
*/


/* JWT Json Web Token
    JWT是token的一种具体实现方式
    官网: https://jwt.io

    jwt是一种三段式加密字符串:
    比如: 'aaaaaaaaaaa.bbbbbbbbbbbbb.ccccccccccccc'

    第一段: header头信息,是一个json对象,使用base64编码 
    header: {
        "alg": "HS256", // 加密算法
        "typ": "JWT" // jwt类型的token
    }
    // base64(header) => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'

    第二段: payload有效载荷,是一个json对象,使用base64编码
    payload: {
        // 预设的参数
        "sub": "1234567890", // 主题
        "name": "John Doe", // 名称
        "iat": 1516239022 // 发布时间
        "exp": 过期时间,
        ...
        // 自定义参数(存储用户信息)
        "username": "用户名",
        "nickname": "用户昵称",
        "userSex": "用户性别"
        ...不要存储敏感信息
    }
    base64(payload) => 'eyJzdWIiOiIxMjM0NTY3OIjoxNTE2MjM5MDIyfQ'

    第三段: signature签名
        1.使用base64编码后的header+payload,
        2.然后再加上secret口令
        3.再使用'HS256'算法生成哈希值
        哈希值 = HS256( base64(header)+'.'+base64(payload),'加密口令' )

    token = 'header.payload.signature'


    在nodejs中使用 jwt 
    1.安装: npm i jsonwebtoken
    2.导入: require()
    3.生成token(返回给前端)
    4.验证token(服务端验证)
*/

// 导入jwt
// let jwt = require('jsonwebtoken')

// 生成token字符串
// jwt.sign({要保存的信息},加密口令,其他设置)
// 要保存的信息: 一般保存用户相关的信息
// 加密口令: 加密和解密时使用的口令
// 其他设置: {expiresIn:有效期} 有效期/秒  '1d' 1天  '7d' 7天

// 要保存的用户信息
// let userInfo = {
//     "username": "xiaocuo",
//     "nickname": "小错"
// }
// const token = jwt.sign(userInfo,'xixihaha',{expiresIn:3})
// console.log( token )
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InhpYW9jdW8iLCJuaWNrbmFtZSI6IuWwj-mUmSIsImlhdCI6MTY1OTE2OTE1MiwiZXhwIjoxNjU5MTY5MTU1fQ.WRJUyltdjGJW3yNCCiYx0OPSxbHCwVuEISEpL-6-_sU'

// 验证token字符串
// jwt.verify(token,加密口令,回调函数)
// token: 要验证的token字符串
// 加密口令: 生成token时的加密口令
// 回调函数: 验证完成之后执行的回调函数

// 成功的验证:
// setTimeout(()=>{
//     jwt.verify(token,'xixihaha',(err,data)=>{
//         // err 验证失败时,是一个错误对象,默认为null
//         // data 从token解析出来的用户信息 payload
//         if (err) {
//             console.log( '验证失败',err )
//         } else {
//             console.log( '验证成功',data )
//         }
//     })
// },2000)


// 失败的验证:过期
// setTimeout(()=>{
//     jwt.verify(token,'xixihaha',(err,data)=>{
//         // err 验证失败时,是一个错误对象,默认为null
//         // data 从token解析出来的用户信息 payload
//         if (err) {
//             console.log( '验证失败',err )// TokenExpiredError: jwt expired
//         } else {
//             console.log( '验证成功',data )
//         }
//     })
// },4000)

// 失败的验证:伪造token
// setTimeout(()=>{
//     jwt.verify(token+'abc','xixihaha',(err,data)=>{
//         // err 验证失败时,是一个错误对象,默认为null
//         // data 从token解析出来的用户信息 payload
//         if (err) {
//             console.log( '验证失败',err )// JsonWebTokenError: invalid signature
//         } else {
//             console.log( '验证成功',data )
//         }
//     })
// },2000)

// 失败的验证:错误的口令
// setTimeout(()=>{
//     jwt.verify(token,'hehehaha',(err,data)=>{
//         // err 验证失败时,是一个错误对象,默认为null
//         // data 从token解析出来的用户信息 payload
//         if (err) {
//             console.log( '验证失败',err )// JsonWebTokenError: invalid signature
//         } else {
//             console.log( '验证成功',data )
//         }
//     })
// },2000)


// 配置cors跨域
app.use(cors())

// 或者

// 配置cors跨域,设置头部信息允许跨域
// app.all("*",function(req,res,next){
//     //设置允许跨域的域名，*代表允许任意域名访问
//     res.header("Access-Control-Allow-Origin","*")
//     // //允许的header类型
//     res.header("Access-Control-Allow-Headers","Content-Type")
//     // //允许的跨域请求方式 
//     res.header("Access-Control-Allow-Methods","POST,GET")
//     next()
// })

// 解析请求体数据
app.use(express.json())// 解析JSON数据
app.use(express.urlencoded({extended:false}))// 解析表单数据

// 使用路由表去匹配客户端的请求
app.use('/user',userRouter)
app.use('/goods',goodsRouter)
app.use('/file',fileRouter)

// 设置静态资源目录
app.use('/static',express.static(path.join(__dirname,'www')))


/* 
    客户端的cookie会随着http请求自动携带到服务端
    在服务端也可以获取或设置cookie数据 (设置/获取都是操作客户端的cookie)
    使用 cookie-parser 插件操作cookie
    安装: npm i cookie-parser
    导入
    使用
    使用cookie-parser之后:
        1.req请求对象中添加了一个cookies属性(包含前端携带过来的cookie数据)
        2.res响应对象中添加了一个cookie()方法(用于设置客户端的cookie数据)
*/
/* // 导入
let cookieParser = require('cookie-parser')
// 使用
app.use(cookieParser())

// 测试cookie
app.get('/test-cookie',(req,res)=>{
    // 1.req请求对象中添加了一个cookies属性(包含前端携带过来的cookie数据)
    console.log( req.cookies )// { user1: 'xiaohong', user2: 'xiaowang' }

    // 2.res响应对象中添加了一个cookie()方法(用于设置客户端的cookie数据)
    res.cookie('user3','xiaofang',{
        maxAge: 1000*60*30, // 设置有效期,单位 毫秒
        domain: 'localhost', // 设置域名
        path: '/' // 设置路径
    })

    res.send('test cookie is ok')
}) */


// 监听端口
app.listen(8080,()=>{
    console.log( '---- server satrt url:http://localhost:8080 ----' )
})
